<script setup lang="ts">
// Spec: 在页面中展示的规格状态的类型
import type { Data } from "@/stores/GoodsStore";
import type { Sku, Spec } from "@/types/Goods";
import bwPowerSet from "@/vendors/power-set";

// specs: 在页面中展示的规格状态
const props = defineProps<{
  specs: Spec[];
  skus: Sku[];
  skuId?: string;
}>();

//声明当前组件触发的自定义事件
const emit = defineEmits<{
  (e: "complete", data: Data): void;
  (e: "incomplete"): void;
}>();

//规格选项的界面状态
interface UIStatus {
  //选中
  selected: boolean;
  //禁用
  disabled: boolean;
}

//创建规格选项的界面状态
function createUIStatus(specs: Spec[]) {
  //specs真实的规格数据，我们要遍历真实的规格数组产生对应的映射数据
  //UIStatus 用于存储最终的映射数据
  const UIStatus: UIStatus[][] = [];
  //遍历真实规格数据
  specs.forEach((spec) => {
    //创建每一个规格种类对应的数组
    const group: UIStatus[] = [];
    //遍历具体的规格选项
    spec.values.forEach(() => {
      //设置每一个规格选项的选中状态和禁用状态（初始值）
      group.push({ selected: false, disabled: false });
    });
    //将规格组对象添加到拷贝结果数组中
    UIStatus.push(group);
  });
  //返回最终的映射数据
  return UIStatus;
}

const UIStatus = reactive(createUIStatus(props.specs));
//当用户选择规格时执行
function updateSelected(index: number, i: number) {
  //获取当前用户点击的规格
  const current = UIStatus[index][i];
  //获取当前用户点击的规格对应的规格组
  const group = UIStatus[index];

  //如果当前规格已经是禁用状态，不能被选择，所以阻止代码继续执行
  if (current.disabled) return;
  //判断用户当前选择的规格是否是选中的
  if (current.selected) {
    //取消选中
    current.selected = false;
  } else {
    //当前用户选择的规格没有被选中
    //先清空当前规格种类中所有规格的选中状态
    group.forEach((item) => {
      item.selected = false;
    });
    //让当前用户状态选中
    current.selected = true;
  }
  //实现用户选择时的禁用效果
  setDisableUIState();
}

//规格查询对象的类型
interface pathMap {
  [key: string]: string | null;
}
//创建规格查询对象
const pathMap = createPathMap();
//创建规格查询对象
function createPathMap(): pathMap {
  //规格查询对象
  const pathMap: pathMap = {};

  //将有库存的规格组合过滤出来
  props.skus
    .filter((sku) => sku.inventory > 0)
    .forEach((sku) => {
      const valueNames = sku.specs.map((spec) => spec.valueName);
      //获取当前规格中用户可能选择的所有的规格
      const sets = bwPowerSet(valueNames).filter((item) => item.length > 0);
      //获取完整规格的长度
      const max = valueNames.length;
      //遍历用户可以选择的所有可能的规格及规格组合
      sets.forEach((set) => {
        //将规格名称以"_"进行拼接
        const key = set.join("_");
        //用于判断当前规格是否是完整的
        const isCompleted = set.length === max;
        //判断规格查询对象中是否已经存储了当前规格
        if (!(key in pathMap)) {
          //如果当前规格是完整的
          if (isCompleted) {
            //将当前规格或规格组合添加到规格查询对象中并赋值sku id
            pathMap[key] = sku.id;
          } else {
            pathMap[key] = null;
          }
        }
      });
    });

  return pathMap;
}

//设置规格的禁用状态
function setDisableUIState() {
  //遍历规格组
  props.specs.forEach((spec, index) => {
    //获取用户选择的规格名称数组
    const selected = getSelected();

    //遍历每一个具体的规格
    spec.values.forEach((value, i) => {
      //如果当前规格已经被选中，说明它可以选，不需要被禁用
      if (UIStatus[index][i].selected) return;
      //将当前选择名称放入用户选择的规格数组名称中，待匹配
      selected[index] = value.name;
      //得到匹配的key
      const key = selected.filter((name) => name).join("_");

      //如不能选择，设置当前规格的disabled为true
      UIStatus[index][i].disabled = !(key in pathMap);
    });
  });
}

//获取用户选择的规格名称数组
function getSelected() {
  //用于按照顺序存储用户选择的规格
  const names: (string | undefined)[] = [];
  //遍历真实的规格数据  从对应的映射数据中判断当前规格是否是选中的
  props.specs.forEach((spec, index) => {
    const selectedIndex = UIStatus[index].findIndex((item) => item.selected);
    //判断当前规格种类中是否有选择的规格
    if (selectedIndex !== -1) {
      names[index] = spec.values[selectedIndex].name;
    } else {
      names[index] = undefined;
    }
  });
  // console.log(names);

  return names;
}

//设置规格默认选中
function setSpecDefaultSelected() {
  if (typeof props.skuId !== "undefined") {
    //查找默认选中的规格对象，从中获取规格名称
    const sku = props.skus.find((sku) => {
      return sku.id === props.skuId;
    });

    // 如果没有找到默认选中的规格对象, 阻止程序继续运行
    if (typeof sku === "undefined") return;
    console.log(1);
    // 获取默认选中的规格名称数组
    const valueNames = sku.specs.map((spec) => spec.valueName);
    console.log(valueNames);

    // 遍历页面中展示的规格(组)
    props.specs.forEach((spec, index) => {
      // 遍历页面中展示的每一个具体的规格
      spec.values.forEach((value, i) => {
        // 查看当前遍历的规格的名称是否被包含在默认选中的规格名称数组中
        if (valueNames.includes(value.name)) {
          // 设置当前规格的选中状态
          UIStatus[index][i].selected = true;
        }
      });
    });
  }
}

//向父组件传递商品信息
function sendGoodsInfoToParent() {
  // 获取用户选择的规格名称数组
  const selected = getSelected().filter((name) => name);
  // 判断用户是否选择了完整的规格信息
  if (selected.length === props.specs.length) {
    // 获取规格 skuId
    const skuId = pathMap[selected.join("_")]!;
    // 根据 skuId 查找规格对象
    const sku = props.skus.find((sku) => sku.id === skuId)!;
    // 如果找到了规格对象
    if (typeof sku !== "undefined") {
      // 触发自定义事件, 向外部传递最新商品信息
      emit("complete", {
        price: sku.price,
        oldPrice: sku.oldPrice,
        inventory: sku.inventory,
        skuId: skuId,
      });
    }
  } else {
    // 用户没有选择完整的规则
    emit("incomplete");
  }
}

setSpecDefaultSelected();
createPathMap();
//更新规格的禁用状态（初始化）
setDisableUIState();
</script>

<template>
  <div class="goods-sku">
    <dl v-for="(spec, index) in specs" :key="spec.id">
      <dt>{{ spec.name }}</dt>
      <dd>
        <template v-for="(value, i) in spec.values">
          <img
            v-if="value.picture"
            :class="{
              selected: UIStatus[index][i].selected,
              disabled: UIStatus[index][i].disabled,
            }"
            :src="value.picture"
            :alt="value.name"
            @click="
              updateSelected(index, i);
              sendGoodsInfoToParent();
            "
          />
          <span
            :class="{
              selected: UIStatus[index][i].selected,
              disabled: UIStatus[index][i].disabled,
            }"
            @click="
              updateSelected(index, i);
              sendGoodsInfoToParent();
            "
            v-else
            >{{ value.name }}</span
          >
        </template>
      </dd>
    </dl>
  </div>
</template>

<style scoped lang="less">
.sku-state-mixin () {
  border: 1px solid #e4e4e4;
  margin-right: 10px;
  cursor: pointer;
  margin-bottom: 10px;
  &.selected {
    border-color: @xtxColor;
  }
  &.disabled {
    opacity: 0.6;
    border-style: dashed;
    cursor: not-allowed;
  }
}
.goods-sku {
  padding-left: 10px;
  padding-top: 20px;
  dl {
    display: flex;
    padding-bottom: 5px;
    align-items: center;
    dt {
      width: 50px;
      color: #999;
    }
    dd {
      flex: 1;
      color: #666;
      > img {
        width: 50px;
        height: 50px;
        .sku-state-mixin ();
      }
      > span {
        display: inline-block;
        height: 30px;
        line-height: 28px;
        padding: 0 20px;
        .sku-state-mixin ();
      }
    }
  }
}
</style>
